package com.colin.library.view.custom;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;

import com.colin.library.view.help.MetricsHelp;
import com.colin.library.view.help.ViewLogHelp;

/**
 * Created by Colin on 2017/2/22.
 */

public class CustomViewOnDraw extends View {


    private Paint mPaint;
    private Paint paintText;
    private Paint paintDrawCircle;

    private Context mContext;
    private int circleCenterX, circleCenterY;
    private int circleRadius;
    private final static float RADIUS_RATIO = 2 / 3f;


    //一般在直接New一个View的时候调用。
    public CustomViewOnDraw(Context context) {
        this(context, null, 0);
    }

    //一般在layout文件中使用的时候会调用，关于它的所有属性(包括自定义属性)都会包含在attrs中传递进来。
    public CustomViewOnDraw(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomViewOnDraw(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        initPaint();
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mPaint = new Paint();
        mPaint.setStrokeWidth(MetricsHelp.dp2px(mContext, 2));
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setColor(Color.WHITE);
        //设置画笔模式为填充
        mPaint.setStyle(Paint.Style.STROKE);


        paintText = new Paint();
        //文字颜色
        paintText.setColor(Color.BLACK);
        //文字大小
        paintText.setTextSize(MetricsHelp.sp2px(mContext, 12));
        //是否添加下划线
        paintText.setUnderlineText(false);
        //Paint.Style.FILL：填充内部;Paint.Style.FILL_AND_STROKE填充内部和描边;Paint.Style.STROKE  ：描边
        paintText.setStyle(Paint.Style.FILL);
        //设置抗锯齿性
        paintText.setAntiAlias(true);


        paintDrawCircle = new Paint();
        paintDrawCircle.setColor(Color.BLUE);
        paintDrawCircle.setStrokeWidth(MetricsHelp.dp2px(mContext, 8));     //为了实验效果明显，特地设置描边宽度非常大


    }

    /**
     * View的大小不仅由自身所决定，同时也会受到父控件的影响，为了我们的控件能更好的适应各种情况，一般会自己进行测量
     * <p>
     * UNSPECIFIED	00	默认值，父控件没有给子view任何限制，子View可以设置为任意大小。
     * EXACTLY	    01	表示父控件已经确切的指定了子View的大小。
     * AT_MOST	    10	表示子View具体大小没有尺寸限制，但是存在上限，上限一般为父View大小。
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        //处理 wrap_content问题
        int defaultDimension = MetricsHelp.dp2px(mContext, 100);

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(defaultDimension, defaultDimension);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(defaultDimension, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, defaultDimension);
        }
    }

    /**
     * 在测量完View并使用setMeasuredDimension函数之后View的大小基本上已经确定了，那么为什么还要再次确定View的大小呢？
     * 这是因为View的大小不仅由View本身控制，而且受父控件的影响，所以我们在确定View大小的时候最好使用系统提供的onSizeChanged回调函数。
     *
     * @param w    View宽度
     * @param h    View高度
     * @param oldw 上一次宽度
     * @param oldh 上一次高度
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        circleCenterX = w / 2;
        circleCenterY = h / 2;

        //处理padding情况
        circleRadius = (int) (Math.min(Math.min(circleCenterY - getPaddingTop(), circleCenterY - getPaddingBottom()),
                Math.min(circleCenterX - getPaddingLeft(), circleCenterX - getPaddingRight())) * RADIUS_RATIO);
    }

    @Override
    public void layout(int l, int t, int r, int b) {
        ViewLogHelp.d("layout");
        super.layout(l, t, r, b);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        ViewLogHelp.d("onLayout");
        super.onLayout(changed, left, top, right, bottom);
    }

    /**
     * Canvas的常用操作速查表
     * 绘制颜色	    drawColor, drawRGB, drawARGB	使用单一颜色填充整个画布
     * 绘制基本形状	drawPoint, drawPoints, drawLine, drawLines, drawRect, drawRoundRect, drawOval, drawCircle, drawArc	依次为 点、线、矩形、圆角矩形、椭圆、圆、圆弧
     * 绘制图片	    drawBitmap, drawPicture	绘制位图和图片
     * 绘制文本	    drawText, drawPosText, drawTextOnPath	依次为 绘制文字、绘制文字时指定每个文字位置、根据路径绘制文字
     * 绘制路径	    drawPath	绘制路径，绘制贝塞尔曲线时也需要用到该函数
     * 顶点操作	    drawVertices, drawBitmapMesh	通过对顶点操作可以使图像形变，drawVertices直接对画布作用、 drawBitmapMesh只对绘制的Bitmap作用
     * 画布剪裁	    clipPath, clipRect	设置画布的显示区域
     * 画布快照	    save, restore, saveLayerXxx, restoreToCount, getSaveCount	依次为 保存当前状态、 回滚到上一次保存的状态、 保存图层状态、 回滚到指定状态、 获取保存次数
     * 画布变换	    translate, scale, rotate, skew	依次为 位移、缩放、 旋转、错切
     * Matrix(矩阵)	getMatrix, setMatrix, concat	实际上画布的位移，缩放等操作的都是图像矩阵Matrix， 只不过Matrix比较难以理解和使用，故封装了一些常用的方法。
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制画布/背景颜色
        canvas.drawColor(Color.GREEN);
        drawPoint(canvas);
        drawLine(canvas);
        drawRect(canvas);
        drawOval(canvas);
        drawCircle(canvas);
        drawAngle(canvas);

//        drawCD(canvas);
    }
    /**
     * 停止线程或动画
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
    }

    /**
     * 可以绘制一个点，也可以绘制一组点
     * 坐标原点默认在左上角，水平向右为x轴增大方向，竖直向下为y轴增大方向
     *
     * @param canvas
     */
    private void drawPoint(Canvas canvas) {
        canvas.drawPoint(100, 100, mPaint);     //在坐标(200,200)位置绘制一个点
        canvas.drawPoints(new float[]{          //绘制一组点，坐标位置由float数组指定
                200, 100,
                300, 100,
                400, 100
        }, mPaint);
        drawText(canvas, "画点", 600, 100);
    }

    /**
     * 绘制直线需要两个点，初始点和结束点，同样绘制直线也可以绘制一条或者绘制一组
     *
     * @param canvas
     */
    private void drawLine(Canvas canvas) {
        // 在坐标(100, 200)(300, 200)之间绘制一条直线
        canvas.drawLine(100, 200, 300, 200, mPaint);
        // 绘制一组线 每四数字(两个点的坐标)确定一条线
        canvas.drawLines(new float[]{
                310, 200, 410, 200,
                420, 200, 520, 200
        }, mPaint);
        drawText(canvas, "直线", 600, 200);
    }

    /**
     * 绘制矩形：
     * 确定确定一个矩形最少需要四个数据，就是对角线的两个点的坐标值，这里一般采用左上角和右下角的两个点的坐标。
     * <p>
     * 关于绘制矩形，Canvas提供了三种重载方法，
     * 第一种就是提供四个数值(矩形左上角和右下角两个点的坐标)来确定一个矩形进行绘制。
     * 其余两种是先将矩形封装为Rect或RectF(实际上仍然是用两个坐标点来确定的矩形)，然后传递给Canvas绘制
     *
     * @param canvas
     */
    private void drawRect(Canvas canvas) {
        // 第一种
        canvas.drawRect(100, 300, 200, 400, mPaint);

        // 第二种
        Rect rect = new Rect(220, 300, 320, 400);
        canvas.drawRect(rect, mPaint);

        // 第三种
        RectF rectF = new RectF(340, 300, 540, 400);
        canvas.drawRect(rectF, mPaint);


        drawText(canvas, "画矩形", 600, 400);

        //绘制圆角矩形
        //既然是圆角矩形，他的角肯定是圆弧(圆形的一部分)，我们一般用什么确定一个圆形呢？
        //圆心 和 半径，其中圆心用于确定位置，而半径用于确定大小
        //圆角矩形的角实际上不是一个正圆的圆弧，而是椭圆的圆弧，这里的两个参数实际上是椭圆的两个半径
        //计算可知我们上次绘制的矩形宽度为700，高度为300，当你让 rx大于350(宽度的一半)， ry大于150(高度的一半) 时奇迹就出现了，
        // 你会发现圆角矩形变成了一个椭圆， 他们画出来是这样的 ( 为了方便确认我更改了画笔颜色， 同时绘制出了矩形和圆角矩形 )：


        // 第一种
        RectF rectFCircle = new RectF(100, 500, 300, 600);
        canvas.drawRoundRect(rectFCircle, 30, 30, mPaint);

        // 第二种
        if (Build.VERSION.SDK_INT >= 21) {
            canvas.drawRoundRect(320, 500, 520, 600, 30, 30, mPaint);
        }

        drawText(canvas, "绘制圆角矩形", 600, 600);
    }

    /**
     * 计算可知我们上次绘制的矩形宽度为700，高度为300，当你让 rx大于350(宽度的一半)， ry大于150(高度的一半) 时奇迹就出现了， 你会发现圆角矩形变成了一个椭圆， 他们画出来是这样的 ( 为了方便确认我更改了画笔颜色， 同时绘制出了矩形和圆角矩形 )：
     *
     * @param canvas
     */
    private void drawOval(Canvas canvas) {
        // 第一种
        RectF rectF = new RectF(100, 700, 250, 800);
        canvas.drawOval(rectF, mPaint);

        // 第二种
        if (Build.VERSION.SDK_INT >= 21) {
            canvas.drawOval(270, 700, 400, 800, mPaint);
        }
        // 第三种：实际上在rx为宽度的一半，ry为高度的一半时，刚好是一个椭圆，通过上面我们分析的原理推算一下就能得到，
        if (Build.VERSION.SDK_INT >= 21) {
            canvas.drawRoundRect(420, 700, 580, 800, 80, 50, mPaint);
        }
        drawText(canvas, "绘制椭圆", 600, 800);
    }

    /**
     * 绘制圆
     * 绘制圆形有四个参数，前两个是圆心坐标，第三个是半径，最后一个是画笔
     *
     * @param canvas
     */
    private void drawCircle(Canvas canvas) {
        //方法1
        // 描边加填充
        paintDrawCircle.setStyle(Paint.Style.FILL_AND_STROKE);
        canvas.drawCircle(150, 950, 50, paintDrawCircle);
        // 描边
        paintDrawCircle.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(280, 950, 50, paintDrawCircle);// 绘制一个圆心坐标在(150,950)，半径为50 的圆。
        // 填充
        paintDrawCircle.setStyle(Paint.Style.FILL);
        canvas.drawCircle(410, 950, 50, paintDrawCircle);


        //方法2
        if (Build.VERSION.SDK_INT >= 21) {
            canvas.drawRoundRect(490, 900, 590, 1000, 50, 50, mPaint);
        }
        drawText(canvas, "绘制圆", 600, 1000);
    }

    /**
     * 绘制圆弧：
     * startAngle  开始角度
     * sweepAngle  扫过角度
     * useCenter   是否使用中心
     *
     * @param canvas
     */
    private void drawAngle(Canvas canvas) {
        // 第一种:public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint){}

        // 第二种:public void drawArc(float left, float top, float right, float bottom, float startAngle,float sweepAngle, boolean useCenter, @NonNull Paint paint) {}

        RectF rectF = new RectF(100, 1100, 300, 1200);
        // 绘制背景矩形
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(rectF, mPaint);

        // 绘制圆弧
        mPaint.setColor(Color.BLUE);
        //不使用中心
        canvas.drawArc(rectF, 0, 90, false, mPaint);
        //使用中心
        canvas.drawArc(rectF, 180, 90, true, mPaint);


        RectF rectfSquare = new RectF(400, 1100, 500, 1200);
        // 绘制背景矩形
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(rectfSquare, mPaint);

        //不使用中心
        canvas.drawArc(rectfSquare, 0, 90, false, mPaint);
        //使用中心
        canvas.drawArc(rectfSquare, 180, 90, true, mPaint);

        drawText(canvas, "绘制圆弧", 600, 1200);
    }

    private void drawText(Canvas canvas, String text, int x, int y) {
        canvas.drawText(text, x, y, paintText);
    }


    /**
     * 画 CD效果
     *
     * @param canvas
     */
    private void drawCD(Canvas canvas) {
        //画大圆
        canvas.drawCircle(circleCenterX, circleCenterY, circleRadius, mPaint);
        //画中心小圆圆
        canvas.drawCircle(circleCenterX, circleCenterY, MetricsHelp.dp2px(mContext, 4), mPaint);
        // 下面是画弧线
        RectF rectF = new RectF(circleCenterX - circleRadius * RADIUS_RATIO, circleCenterY - circleRadius * RADIUS_RATIO,
                circleCenterX + circleRadius * RADIUS_RATIO, circleCenterY + circleRadius * RADIUS_RATIO);

        canvas.drawArc(rectF, 0, 80, false, mPaint);
        canvas.drawArc(rectF, 180, 80, false, mPaint);


        rectF = new RectF(circleCenterX - circleRadius / 2, circleCenterY - circleRadius / 2,
                circleCenterX + circleRadius / 2, circleCenterY + circleRadius / 2);

        canvas.drawArc(rectF, 0, 80, false, mPaint);
        canvas.drawArc(rectF, 180, 80, false, mPaint);
    }

    //开始动画
    public void startAnimator() {
        post(new Runnable() {
            @Override
            public void run() {
                RotateAnimation animation = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
                animation.setInterpolator(new LinearInterpolator());
                animation.setRepeatCount(-1);
                animation.setDuration(1000);
                animation.setFillAfter(true);
                startAnimation(animation);
            }
        });
    }

    /**
     * 对外公开颜色值
     *
     * @param color
     */
    public void setPaintColor(int color) {
        mPaint.setColor(color);
    }
}
